﻿//TODO: implement, Bandpass

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// This should be a classical bandpass filter.
    /// </summary>
    public class Bandpass : BaseImageFilter
    {
        //filter fc = 2hz, fs = 10hz

        protected int m_nLowPassOrder = 4;

        protected double[] m_aInputValueModifier = null;
        protected double[] m_aOutputValueModifier = null;
        protected double[] m_aInputValue = null;
        protected double[] m_aOutputValue = null;
        protected int m_nValuePosition = 0;

        /// <summary>
        /// Initializes a new instance of the <see cref="Bandpass"/> class.
        /// </summary>
        public Bandpass()
        {
            m_aInputValueModifier = new double[m_nLowPassOrder];
            m_aInputValueModifier[0] = 0.098531160923927;
            m_aInputValueModifier[1] = 0.295593482771781;
            m_aInputValueModifier[2] = 0.295593482771781;
            m_aInputValueModifier[3] = 0.098531160923927;

            m_aOutputValueModifier = new double[m_nLowPassOrder];
            m_aOutputValueModifier[0] = 1.0;
            m_aOutputValueModifier[1] = -0.577240524806303;
            m_aOutputValueModifier[2] = 0.421787048689562;
            m_aOutputValueModifier[3] = -0.0562972364918427;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Bandpass"/> class.
        /// </summary>
        /// <param name="_bandpass">The _bandpass.</param>
        internal Bandpass(Bandpass _bandpass)
        {
            this.m_aInputValueModifier = _bandpass.m_aInputValueModifier;
            this.m_aOutputValueModifier = _bandpass.m_aOutputValueModifier;
            this.m_nLowPassOrder = _bandpass.m_nLowPassOrder;
        }

        /// <summary>
        /// Gets or sets the input value modifier.
        /// </summary>
        /// <value>The input value modifier.</value>
        public double[] InputValueModifier
        {
            get { return m_aInputValueModifier; }
            set { m_aInputValueModifier = value; }
        }

        /// <summary>
        /// Gets or sets the output value modifier.
        /// </summary>
        /// <value>The output value modifier.</value>
        public double[] OutputValueModifier
        {
            get { return m_aOutputValueModifier; }
            set { m_aOutputValueModifier = value; }
        }

        /// <summary>
        /// Gets or sets the low passorder.
        /// </summary>
        /// <value>The low passorder.</value>
        public int LowPassorder
        {
            get { return m_nLowPassOrder; }
            set { m_nLowPassOrder = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap bitmap = _bitmap;

            int nWidth = bitmap.Width;
            int nHeight = bitmap.Height;

            int nValue = 0;

            int y = 0;
            int x = 0;
            double dFilter = -1.0f;

            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    nValue = bitmap.GetGrayPixelValue(x, y);
                    dFilter = Filter(nValue);

                    nValue = (int)dFilter;
                    if (nValue < 0) nValue = 0;
                    if (nValue > 255) nValue = 255;
                    bitmap.SetPixel(x, y, Color.FromArgb(nValue, nValue, nValue));
                }
            }
            return _bitmap;
        }

        /// <summary>
        /// Bandpass filter routine...
        /// </summary>
        /// <param name="inputValue"></param>
        /// <returns></returns>
        public double Filter(double inputValue)
        {
            int i = 0;
            if (this.m_aInputValue == null && this.m_aOutputValue == null)
            {
                this.m_aInputValue = new double[m_nLowPassOrder];
                this.m_aOutputValue = new double[m_nLowPassOrder];

                m_nValuePosition = -1;

                for (i = 0; i < m_nLowPassOrder; i++)
                {
                    this.m_aInputValue[i] = inputValue;
                    this.m_aOutputValue[i] = inputValue;
                }

                return inputValue;
            }
            else if (this.m_aInputValue != null && this.m_aOutputValue != null)
            {
                m_nValuePosition = IncrementLowOrderPosition(m_nValuePosition);

                this.m_aInputValue[m_nValuePosition] = inputValue;
                this.m_aOutputValue[m_nValuePosition] = 0;

                int j = m_nValuePosition;

                for (i = 0; i < m_nLowPassOrder; i++)
                {
                    this.m_aOutputValue[m_nValuePosition] += m_aInputValueModifier[i] * this.m_aInputValue[j] -
                        m_aOutputValueModifier[i] * this.m_aOutputValue[j];

                    j = DecrementLowOrderPosition(j);
                }

                return this.m_aOutputValue[m_nValuePosition];
            }
            else
            {
                throw new Exception("Both inputValue and outputValue should either be null or not null.  This should never be thrown.");
            }
        }

        /// <summary>
        /// Decrements the low order position.
        /// </summary>
        /// <param name="j">The j.</param>
        /// <returns></returns>
        private int DecrementLowOrderPosition(int j)
        {
            if (--j < 0)
            {
                j += m_nLowPassOrder;
            }
            return j;
        }

        /// <summary>
        /// Increments the low order position.
        /// </summary>
        /// <param name="position">The position.</param>
        /// <returns></returns>
        private int IncrementLowOrderPosition(int position)
        {
            return ((position + 1) % m_nLowPassOrder);
        }

        /// <summary>
        /// Creates a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Bandpass(this);
        }
    }
}
